# -*- coding;utf-8 -*-
"""
File name : ReadDocx2QuestionListVocational.py
Program IDE : PyCharm
Create file time: 2023/10/3 21:54
File Create By Author : 朱清闯
"""

try:
    from xml.etree.cElementTree import XML
except ImportError:
    from xml.etree.ElementTree import XML
import zipfile
import xml.dom.minidom as xdom
import re
from utils.uploadfile import get_new_name
import shutil
import os
from django.conf import settings
from PIL import Image


grade_rule_list = [r'(高考|高一|高二|高三)', r'(中考|九年级|八年级|七年级)', r'(六年级|五年级|四年级|三年级|二年级|一年级)']
grade_rule = grade_rule_list[settings.SEGMENT]
blank_char = re.compile('[\u3000|\xa0]')
question_choice_or_cloze_rule = r'^[一二三四]\s*、\s*(选择题|单项选择题|填空题|非选择题|多项选择题|判断题|是非选择题)\s*[\(（]'  # 匹配选择题或者填空题如一、选择题 二、填空题
question_type_rule = r"^[一二三四]\s*、\s*(选择题|单项选择题|填空题|非选择题|多项选择题|判断题|是非选择题)\s*[\(（]"  # 匹配题型，如一、选择题
question_number_rule = r"^(\.|．){0,3}(\d+)\s*(\.|．){1}\s*(.+)"  # 匹配小题号，如段首以1.开头, 并跳过开头加重点符号
answer_rule = r"^【答案】(.*)"  # 匹配答案，如【答案】xxx
explain_rule = r'^【解析】(.*)'  # 匹配解析，如【解析】xxx
difficulty_rule = r'^【难度】(\d)'
pattern_get_image_target = r"media/image\d{1,2}(.\w+)"
patten_image_name = r'media/(image\d{1,2}.\w+)'
pattern_del_tag = re.compile(r'<[^>]+>')  # 去掉标签<w:t>
pattern_get_text_between = r'>(.+)<'
pattern_get_imageId = r"(rId[\d+])"
pattern_date_rule = r'20(1|2)\d[-年]\d{1,2}[-月]\d{1,2}日*'
pattern_date_year_rule = r'(20\d{2})\s*(年|届|学年)'
pattern_date_month_rule = r'(\d{1,2})\s*月'
precondition_rule = r'阅读(下列|以下)材料[,，]\s*回答第\s*(\d{1,2})\s*[至-]\s*第{0,1}(\d{1,2})\s*小{0,1}题\s*'
pattern_answer_vocational_rule = re.compile(r'(试题答案)|(参考答案)')
image_dict = {}
type_dic = {'简答题': 0, '选择题': 1, '单项选择题': 1, '填空题': 2, '非选择题': 2, '多项选择题': 3, '判断题': 4, '是非选择题': 4}
grade_dic_list = [{'高三': '3', '高二': '2', '高一': '1', '选考': '4', '高考': '4'},
                  {'中考': '4', '九年级': '3', '八年级': '2', '七年级': '1'},
                  {'六年级': 6, '五年级': 5, '四年级': 4, '三年级': 3, '二年级': 2, '一年级': 1}
                  ]
grade_dic = grade_dic_list[settings.SEGMENT]
paper_dict = {'title': '', 'publish_time': '', 'grade': '3', 'open_level': 'public', 'is_joint': 'true'}
pattern_answer_detach = re.compile(r'(\d+)\s*[．\.]\s*答案\s*[：:](.*)')
pattern_answer_attach = re.compile(r'(\d+)\s*[．\.]\s*【答案】\s*(.*)')
pattern_explain_detach = re.compile(r'(\d+)\s*\.\s*解析\s*[：:](.*)')
pattern_explain_detach2 = re.compile(r'(\d+)\s*\.\s*详解\s*[：:](.*)')
pattern_answer_detach_no_number = re.compile(r'答案\s*[：:](.*)')
pattern_answer_attach_no_number = re.compile(r'【答案】\s*(.*)')
pattern_explain_detach_no_number = re.compile(r'解析\s*[：:](.*)')
pattern_explain_detach_no_number2 = re.compile(r'详解\s*[：:](.*)')
pattern_explain_attach_no_number = re.compile(r'【解析】\s*(.*)')
pattern_explain_attach_no_number2 = re.compile(r'【详解】\s*(.*)')


def find_first_html_tag_index(text):
    # 定义一个正则表达式来匹配HTML标签
    # 这个表达式匹配类似<tag>...</tag>、<tag/>和<tag>的形式
    html_tag_pattern = r'<(\w+)(?:\s[^>]+)?(/>|>)'

    # 在字符串中搜索第一个匹配的标签
    match = re.search(html_tag_pattern, text)

    # 如果找到了匹配项，返回匹配开始的位置
    if match:
        return match.start()
    else:
        # 如果没有找到匹配的标签，返回-1表示未找到
        return -1


def p_add_marked(text_str):
    return '<p>' + text_str + '</p>'


# 图片转标准的html标签图像img, 并且打开图片，获取其宽度和高度，如果宽度或高度太大，re做调整
def img_add_marked(path_str):
    try:
        if path_str[:4] == '<img':
            return path_str
        if '/' in path_str:
            path_str = path_str.split('/')[-1]
        path_image = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.IMAGE_TEMP_URL + path_str
        image = Image.open(path_image)
        MAX_PIXEL_WIDTH = 300
        MAX_PIXEL_HEIGHT = 300
        width = image.width
        height = image.height
        if height > width > MAX_PIXEL_WIDTH:
            height = int(height * MAX_PIXEL_WIDTH / width)
            width = MAX_PIXEL_WIDTH
        if width > height > MAX_PIXEL_HEIGHT:
            width = int(width * height / MAX_PIXEL_HEIGHT)
            height = MAX_PIXEL_HEIGHT
        img_url = settings.SERVER_IP + settings.IMAGE_TEMP_URL + path_str
        # return '<img src="' + img_url + '" width=">'
        return f'<img src="{img_url}" width="{width}" height="{height}" alt="抱歉，服务器繁忙" >'
    except Exception as E:
        print('Exception:', E)


# 将二维数组转换成标准的html表格table
def table_add_marked(array):
    if not isinstance(array, list) or not array or not isinstance(array[0], list):
        raise ValueError("输入不是一个有效的二维列表。")
        # 检查数组是否至少有两行
    if len(array) < 1:
        raise ValueError("输入列表必须至少有1行。")
        # 开始构建HTML表格
    html = '<table style="width: auto;" border="1"><tbody>'
    # 处理标题行
    html += '<tr>'
    for header in array[0]:
        html += '<th>{}</th>'.format(header)
    html += '</tr>'
    # 处理数据行
    for row in array[1:]:
        html += '<tr>'
        for cell in row:
            html += '<td>{}</td>'.format(cell)
        html += '</tr>'
        # 结束表格
    html += '</tbody></table>'
    return html


# 检测题目是否连续，即漏题和增题
def verify_question_number(question_list):
    flag = True
    q_index = 0
    for q_index in range(len(question_list)-1):
        if question_list[q_index]['order'] + 1 != question_list[q_index+1]['order']:
            flag = False
            break
    return flag, q_index+1


def get_table(element):
    w_trs = element.getElementsByTagName("w:tr")
    rows_text_array = []  # 存放行的文本
    for r_index, tr in enumerate(w_trs):
        # 获取所有的单元格
        cells = tr.getElementsByTagName("w:tc")
        cells_text_array = []   # 存放单元格的文本
        for c_index, cell in enumerate(cells):
            # 获取所有的文本
            wts = cell.getElementsByTagName("w:t")
            cell_text_all = ''
            for wt in wts:  # 把文本拼接
                text_temp = re.search(pattern_get_text_between, wt.toxml())
                if text_temp:
                    text_temp = text_temp.group(1).strip()
                    cell_text_all += text_temp + '<br>'
            if len(cell_text_all) > 4:
                cell_text_all = cell_text_all[:-4]
            cells_text_array.append(cell_text_all)
        rows_text_array.append(cells_text_array)
    # print('二维数组表格：', rows_text_array)  # 打印结果，二维数组
    return rows_text_array


# 匹配缩进的值
def retract(tag_string: str) -> int:
    pattern = r'w:firstLineChars="(\d+)"'
    match = re.search(pattern, tag_string)
    if match:
        return int(match.group(1))
    else:
        return 0


# 参数paragraph就是<w:p></w:p>的内容
def get_text_in_paragraph(paragraph):
    """
    获取一个段落内的所有文字
    """
    wts = paragraph.getElementsByTagName("w:t")  # 获取该段落内的所有的可见文本
    p_text = ""
    for wt in wts:
        t_text = pattern_del_tag.sub('', wt.toxml())
        p_text += t_text
    if len(p_text) == 0:
        return ''
    # 根据制表符，计算缩进空格
    retract_value = 0
    # w:wind标签是从其他编辑器直接复制代码到word得到的标签
    winds = paragraph.getElementsByTagName("w:ind")
    if len(winds) > 0:
        wind = winds[0]
        # wind_string = pattern_del_tag.sub('', wind.toxml())
        wind_string = wind.toxml()
        retract_value_temp = retract(wind_string)
        retract_value += retract_value_temp//50
    # w:tab标签是在word直接手工输入tab键得到的
    w_tabs = paragraph.getElementsByTagName("w:tab")
    if len(w_tabs) > 0:
        retract_value += len(w_tabs)*4
    if retract_value > 0:
        # print('缩进：', retract_value)
        p_text = ' '*retract_value + p_text

    return p_text


def get_images_in_paragraph(element):
    """获取一个段落内的所有图片的url，并用-号连接各个url"""
    wds = element.getElementsByTagName("a:blip")  # 获取该段落内的所有的图片
    images = []
    for image in wds:
        image_name = image.getAttribute("r:embed")
        image_url = image_dict[image_name]  # 获取图片rul
        images.append(image_url)
    return images


def store_images_to_dict(root):
    """把所有的图片先保存到字典中，便于后期直接用id对应取出value"""
    relation_ships = root.getElementsByTagName("Relationship")
    for relation_ship in relation_ships:
        target = relation_ship.getAttribute("Target")
        image_id = relation_ship.getAttribute("Id")
        if target and re.match(pattern_get_image_target, target):
            src_file = 'word/' + target
            dst_dir = os.path.join(settings.BASE_DIR).replace('\\', '/') + settings.IMAGE_TEMP_URL
            shutil.copy(src_file, dst_dir)
            suffix = re.match(pattern_get_image_target, target).group(1)
            src_name = dst_dir + re.match(patten_image_name, target).group(1)
            image_name = get_new_name() + suffix
            new_name = dst_dir + image_name
            os.rename(src_name, new_name)
            images_url = settings.IMAGE_TEMP_URL + image_name
            image_dict[image_id] = images_url


# 提取答案
def extract_questions_and_answers(question_string):
    answer_list = []
    # 使用正则表达式匹配题号和答案
    # 假设题号是一个或多个数字，答案是一个或多个字母（转换为大写）
    pattern = r'(\d+)\.\s*([A-Za-z]+)'
    matches = re.findall(pattern, question_string)
    # 遍历匹配结果，并将它们转换为字典后添加到answer_list中
    for number, answer in matches:
        # 将题号转换为整数，答案转换为大写字符串
        answer_dict = {'number': int(number), 'answer': answer.upper()}
        answer_list.append(answer_dict)
    return answer_list


# 是否包含参考答案
def contains_answer_keyword(text):
    # 定义一个正则表达式，匹配'试题答案'或'参考答案'
    # pattern = re.compile(r'试题答案|参考答案')
    # 使用正则表达式的search方法，如果找到匹配项则返回True，否则返回False
    # if pattern.search(text):\
    if '试题答案' in text or '参考答案' in text:
        return True
    else:
        return False


# 职高计算机专用
def get_docx_text_vocational(path):
    precondition_number_start = precondition_number_end = -1
    precondition_content = ""
    precondition_collect_over = True  # 收集前置情境是否结束，True表示结束
    is_answer_section = False
    questions_list = []  # 存放所有题目
    question_index = 0
    result_flag = True
    partly_error = ''
    # 开始切题的标志，当设置题型后，开始切题，跳过注意事项，比如1.所有试题均需在答题纸上作答；该部分跟题号有混淆。
    question_start_flag = False
    content = ''
    # 存放是否来到答案部分
    question_dict = {"content": "", "answer": "", "explain": "", "type": 1, "reference": "", "difficulty_level": 3,
                     "knowledgepoint": -1, "knowledgepoint_cn":  [], 'order': 1, 'bank_point': 1
                     }
    with zipfile.ZipFile(path, 'r') as zfile:
        zfile.extractall()
    # 以下读取所有图片文件，并一id为key,图片在服务器的url为value
    image_rels = xdom.parse('word/_rels/document.xml.rels')
    root1 = image_rels.documentElement
    store_images_to_dict(root1)

    # 初始化拿到第一个body标签
    xp = xdom.parse('word/document.xml')
    root = xp.documentElement
    bodies = root.getElementsByTagName("w:body")
    body = bodies[0]

    current_type_cn = '单项选择题'
    current_type = type_dic[current_type_cn]
    question_dict["type"] = current_type
    question_dict['type_cn'] = current_type_cn
    node_index = 0
    # 第一行试卷的标题填入“reference”字段，并填入paper的标题
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    # 用while是因为跳过空行段落,来到第一行
    while paragraph_text.strip() == '' or not re.search(pattern_date_year_rule, paragraph_text):
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    reference = paragraph_text.strip()
    question_dict["reference"] = reference
    paper_dict['title'] = reference
    # 第一行中提取试卷的时间，字典中填入“publish_time”字段
    publish_time = ''
    publish_year = ''
    if re.search(pattern_date_year_rule, paragraph_text):
        publish_year = re.search(pattern_date_year_rule, paragraph_text).group(1)
        publish_time += publish_year
    if re.search(pattern_date_month_rule, paragraph_text):
        publish_month = re.search(pattern_date_month_rule, paragraph_text).group(1)
        if publish_year != '':
            publish_time = publish_year + "-" + publish_month + "-" + "1"
            paper_dict["publish_time"] = publish_time
            question_dict['publish_time'] = publish_time

    # 第二行之后是题目的题型，字典中填入“type”字段
    node_index += 1
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    while paragraph_text.strip() == '' or not re.search(question_choice_or_cloze_rule, paragraph_text):
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    if re.search(question_choice_or_cloze_rule, paragraph_text):
        current_type_cn = re.search(question_choice_or_cloze_rule, paragraph_text).group(1)
        question_dict["type_cn"] = current_type_cn
        question_dict["type"] = type_dic[current_type_cn]
        question_start_flag = True

    # 从第3行开始收集题目，跳过空行
    node_index += 1
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    while paragraph_text.strip() == '':
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    while node_index < len(body.childNodes):
        element = body.childNodes[node_index]
        ele_name = element.nodeName
        # 以下获取可见文本和图片，因此都在<w:p>便签内
        if ele_name == "w:p":
            p_text = get_text_in_paragraph(element)
            p_text = blank_char.sub(' ', p_text).replace("（", "(").replace('）', ')').replace('，', ',')
            if p_text:
                # 匹配题型，如二、非选择题
                if re.match(question_type_rule, p_text) and not is_answer_section:
                    if question_dict["content"]:
                        questions_list.append(question_dict)
                    current_type_cn = re.match(question_type_rule, p_text).group(1)
                    question_dict = {"content": "", "answer": "", "explain": "", "type": type_dic[current_type_cn],
                                     "type_cn": current_type_cn, 'order': 1, "reference": reference,
                                     "difficulty_level": 3, "knowledgepoint": [],
                                     'publish_time': publish_time, 'bank_point': 1
                                     }
                # 匹配到试题前置情境
                elif re.search(precondition_rule, p_text.strip(), re.M):
                    precondition_number_start = int(re.search(precondition_rule, p_text).group(2))
                    precondition_number_end = int(re.search(precondition_rule, p_text).group(3))
                    precondition_content = p_add_marked(p_text)
                    # 以下是初始化存储前置情境的变量，False表示已经情境范围未结束
                    precondition_collect_over = False
                # 匹配到题号，比如1.以下xxx, 先结算上一题的内容，添加到列表，再清空题目字典，最后把本行文本加入到新题目字典中，以及题号
                elif re.search(question_number_rule, p_text.strip(), re.M) and question_start_flag and not is_answer_section:
                    if question_dict["content"] != '':
                        questions_list.append(question_dict)
                        question_dict = {"content": "", "answer": "", "explain": "", "type_cn": current_type_cn,
                                         'type': type_dic[current_type_cn], "reference": reference,
                                         "difficulty_level": 3, "knowledgepoint":  [], 'publish_time': publish_time,
                                         "knowledgepoint_cn":  [], 'order': 1, 'bank_point': 1}
                    content = re.search(question_number_rule, p_text, flags=re.M).group(4)
                    order = re.search(question_number_rule, p_text, flags=re.M).group(2)
                    # question_dict['content'] = p_add_marked(content)
                    question_dict['order'] = int(order)
                    # print('题号:', order)
                    if precondition_number_start <= int(order) <= precondition_number_end:
                        question_dict['content'] = precondition_content + p_add_marked(content)
                        # 情境超出题号范围，无须处理情境内容，只须把题号后的文本添加到新题的题干中
                    else:
                        question_dict['content'] = p_add_marked(content)
                    if int(order) == precondition_number_end:
                        precondition_content = ""
                    # 停止收集情境文本, 用于遇到普通文本是，判断是否收集到情境中
                    precondition_collect_over = True

                # 匹配到题号，比如1.【答案】，说明匹配到参考答案
                elif re.search(question_number_rule, p_text.strip(), re.M) and is_answer_section:
                    answer_line_list = extract_questions_and_answers(p_text.strip())
                    for item in answer_line_list:
                        order = item['number']
                        write_index = search_order_index_error(questions_list, order)
                        if write_index > -1:
                            answer_content = item['answer']
                            if len(answer_content) <= 4:
                                answer_content = answer_content.strip()
                            questions_list[write_index]['answer'] = p_add_marked(answer_content)
                            questions_list[write_index]['explain'] = p_add_marked('略')
                            # print('答案部分的order:', order)
                        else:
                            partly_error += f'第{order}题的答案未录入<br>'
                            result_flag = False
                # 匹配到参考答案，检测前面的题目切题有没有错误
                elif pattern_answer_vocational_rule.search(p_text.strip()):
                    if question_dict['content'] != '':
                        questions_list.append(question_dict)
                    is_answer_section = True
                    # 停止收集情境文本, 用于遇到普通文本是，判断是否收集到情境中
                    precondition_collect_over = True
                    # print('匹配到参考答案')
                # 非空行的文本内容，且未到参考答案部分，则添加到题干中，否则在参考答案部分，则丢弃
                elif not is_answer_section:
                    if not precondition_collect_over:
                        precondition_content += p_add_marked(p_text)
                    elif question_dict['content'] != '':
                        question_dict['content'] += p_add_marked(p_text)
            # 获取该段落内的所有的图片
            p_images = get_images_in_paragraph(element)
            if p_images:
                for p_image in p_images:
                    p_image_marked = img_add_marked(p_image)
                    if question_dict["content"]:
                        question_dict["content"] += p_image_marked
        # 以下获取表格内容，分类讨论放入题目中或收集答案
        elif ele_name == "w:tbl" and not is_answer_section:
            table = get_table(element)
            if len(table) > 0:
                table_marked = table_add_marked(table)
                if question_dict["content"] != '':
                    question_dict['content'] += table_marked
        # 以下获取表格内容，填入相应题目的答案，这是获取答案的第二种方式
        elif ele_name == "w:tbl" and is_answer_section:
            table = get_table(element)
            if table:
                for row_index in range(0, len(table), 2):
                    for column_index in range(1, len(table[row_index])):
                        # print("索引号：", question_number)
                        # 检测之前的切题的题目和当前答案的题号是否一致，如果有漏题和增题，则返回当前对应好的题目列表
                        order = int(table[row_index][column_index])
                        write_index = search_order_index_error(questions_list, order)
                        if write_index > -1:
                            answer_content = table[row_index + 1][column_index]
                            if len(answer_content) <= 4:
                                answer_content = answer_content.strip()
                            questions_list[write_index]['answer'] = p_add_marked(answer_content)
                            questions_list[write_index]['explain'] = p_add_marked('略')
                            # print('答案部分的order:', order)
                        else:
                            partly_error += f'第{order}题的答案未录入<br>'
                            result_flag = False
        node_index += 1
    return result_flag, partly_error, questions_list, paper_dict


def search_order_index(question_list, order_search):
    index = -1
    for i in range(len(question_list)-1, -1, -1):
        if question_list[i]['order'] == order_search:
            index = i
            break
    return index


def search_order_index_error(questions_list, order):
    write_index = -1
    if order == len(questions_list):
        write_index = len(questions_list) - 1
    # 题号超出试题列表，说明题目有漏题，或者题目不是从第1题开始的，查找当前order在question_list数组中的索引
    else:
        temp_index = search_order_index(questions_list, order)
        if temp_index > -1:
            write_index = temp_index
        else:
            print(f'第{order}题的答案未写入；<br>')
    return write_index


# 普高专用, 题干答案分离
def get_docx_text_detach(path):
    precondition_number_start = precondition_number_end = -1
    precondition_content = ""
    precondition_collect_over = True  # 收集前置情境是否结束，True表示结束
    is_answer_section = False
    questions_list = []  # 存放所有题目
    last_area = ''  # 记录上次填充的区域，共有answer、explain、content三种，用于判断空行文本时，应该填入哪个区域
    order = 0
    write_index = 0
    partly_error = ''   # 记录部分答案错误的反馈信息
    result_flag = True  # 是否存在部分答案未录入，True代表全部正确，False代表部分答案未录入
    # 开始切题的标志，当设置题型后，开始切题，跳过注意事项，比如1.所有试题均需在答题纸上作答；该部分跟题号有混淆。
    question_start_flag = False
    content = ''
    # 存放是否来到答案部分
    question_dict = {"content": "", "answer": "", "explain": "", "type": 1, "reference": "", "difficulty_level": 3,
                     "knowledgepoint":  [], "knowledgepoint_cn":  [], 'order': 1, 'bank_point': 1
                     }
    with zipfile.ZipFile(path, 'r') as zfile:
        zfile.extractall()
    # 以下读取所有图片文件，并一id为key,图片在服务器的url为value
    image_rels = xdom.parse('word/_rels/document.xml.rels')
    root1 = image_rels.documentElement
    store_images_to_dict(root1)

    # 初始化拿到第一个body标签
    xp = xdom.parse('word/document.xml')
    root = xp.documentElement
    bodies = root.getElementsByTagName("w:body")
    body = bodies[0]

    current_type_cn = '选择题'
    current_type = type_dic[current_type_cn]
    question_dict["type"] = current_type
    question_dict['type_cn'] = current_type_cn
    node_index = 0
    has_title = False    # 标记试题是否有标题行，如果没有标题为False;有标题True
    has_question_type = False   # 标记是否题型行，如果没有题型False,否则为True
    # 第一行试卷的标题填入“reference”字段，并填入paper的标题
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    # 用while是因为跳过空行段落,来到第一行
    # 包含没有标题，直接试题开始的特殊情况
    while paragraph_text.strip() == '':
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    reference = paragraph_text.strip()
    question_dict["reference"] = reference
    paper_dict['title'] = reference
    # 第一行中提取年级，并存入"grade"字段
    if re.search(grade_rule, paragraph_text):
        grade = grade_dic[re.search(grade_rule, paragraph_text).group(1)]
        paper_dict["grade"] = grade
        has_title = True
    # 第一行中提取试卷的时间，字典中填入“publish_time”字段
    publish_time = ''
    publish_year = ''
    if re.search(pattern_date_year_rule, paragraph_text):
        publish_year = re.search(pattern_date_year_rule, paragraph_text).group(1)
        publish_time += publish_year
    if re.search(pattern_date_month_rule, paragraph_text):
        publish_month = re.search(pattern_date_month_rule, paragraph_text).group(1)
        if publish_year != '':
            publish_time = publish_year + "-" + publish_month + "-" + "1"
            paper_dict["publish_time"] = publish_time
            question_dict['publish_time'] = publish_time
            has_title = True
    # 第二行之后是题目的题型，字典中填入“type”字段
    # 包含没有题型，直接试题开始的特殊情况
    if has_title:
        node_index += 1
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    while paragraph_text.strip() == '':
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    if re.search(question_choice_or_cloze_rule, paragraph_text):
        current_type_cn = re.search(question_choice_or_cloze_rule, paragraph_text).group(1)
        question_dict["type_cn"] = current_type_cn
        question_dict["type"] = type_dic[current_type_cn]
        question_start_flag = True
        has_question_type = True

    # 从第3行开始收集题目，跳过空行
    # 包含没有题型行，直接试题开始的特殊情况
    if has_question_type:
        node_index += 1
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    while paragraph_text.strip() == '':
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    while node_index < len(body.childNodes):
        element = body.childNodes[node_index]
        ele_name = element.nodeName
        # 以下获取可见文本和图片，因此都在<w:p>便签内
        if ele_name == "w:p":
            p_text = get_text_in_paragraph(element)
            p_text = blank_char.sub(' ', p_text).replace("（", "(").replace('）', ')').replace('，', ',')
            if len(p_text) > 0:
                # 匹配题型，如二、非选择题
                if re.match(question_type_rule, p_text) and not is_answer_section:
                    current_type_cn = re.match(question_type_rule, p_text).group(1)
                    precondition_collect_over = True
                # 匹配到题号，比如1.以下xxx, 先结算上一题的内容，添加到列表，再清空题目字典，最后把本行文本加入到新题目字典中，以及题号
                elif re.search(question_number_rule, p_text.strip(), re.M) and not is_answer_section:
                    if len(question_dict["content"]) > 0:
                        questions_list.append(question_dict)
                        question_dict = {"content": "", "answer": "", "explain": "", "type_cn": current_type_cn,
                                         'type': type_dic[current_type_cn], "reference": reference,
                                         "difficulty_level": 3, "knowledgepoint":  [], 'publish_time': publish_time,
                                         "knowledgepoint_cn":  [], 'bank_point': 1
                                         }
                    # order_last = int(order)
                    content = re.search(question_number_rule, p_text.strip(), flags=re.M).group(4)
                    order = re.search(question_number_rule, p_text.strip(), flags=re.M).group(2)
                    order = int(order)
                    # if order != order_last + 1:
                    #     return_message = f'第{order_last}题之后，出现漏题'
                    #     print(return_message)
                    #     return False, return_message, questions_list, paper_dict
                    question_dict['order'] = int(order)
                    # print('order:', order)
                    last_area = 'content'
                    # 在情境范围内，则情境问题放在最前面，并连接本次题号后的内容
                    if precondition_number_start <= int(order) <= precondition_number_end:
                        question_dict['content'] = precondition_content + p_add_marked(content)
                    # 情境超出题号范围，无须处理情境内容，只须把题号后的文本添加到新题的题干中
                    else:
                        question_dict['content'] = p_add_marked(content)
                    if int(order) == precondition_number_end:
                        precondition_content = ""
                    # 停止收集情境文本, 用于遇到普通文本是，判断是否收集到情境中
                    precondition_collect_over = True
                # 匹配到题号.答案：x或者【答案】x，比如1.答案：x或1.【答案】x。说明已经匹配到答案
                elif (pattern_answer_detach.search(p_text.strip()) or
                      pattern_answer_attach.search(p_text.strip())) and is_answer_section:
                    match = pattern_answer_detach.search(p_text.strip())
                    if not match:
                        match = pattern_answer_attach.search(p_text.strip())
                    order = match.group(1)
                    order = int(order)
                    write_index = search_order_index_error(questions_list, order)
                    if write_index > -1:
                        answer_content = match.group(2)
                        if len(answer_content) <= 4:
                            answer_content = answer_content.strip()
                        questions_list[write_index]['answer'] = p_add_marked(answer_content)
                        last_area = 'answer'
                        # print('答案部分的order:', order)
                    else:
                        partly_error += f'第{order}题的答案未录入<br>'
                        result_flag = False
                # 匹配到答案：x，比如答案：x。说明已经匹配到答案，这是另一种格式
                elif pattern_answer_detach_no_number.search(p_text.strip()) and is_answer_section:
                    write_index = search_order_index_error(questions_list, order)
                    if write_index > -1:
                        match = pattern_answer_detach_no_number.search(p_text.strip())
                        answer_content = match.group(1)
                        if len(answer_content) <= 4:
                            answer_content = answer_content.strip()
                        questions_list[write_index]['answer'] = p_add_marked(answer_content)
                        last_area = 'answer'
                    else:
                        partly_error += f'第{order}题的答案未录入<br>'
                        result_flag = False
                # 匹配到题号.解析：x，比如1.解析：x。说明已经匹配到解析
                elif (pattern_explain_detach.search(p_text.strip()) or pattern_explain_detach2.search(p_text.strip()))\
                        and is_answer_section:
                    match = pattern_explain_detach.search(p_text.strip())
                    if not match:
                        match = pattern_explain_detach2.search(p_text.strip())
                    order = match.group(1)
                    order = int(order)
                    write_index = search_order_index_error(questions_list, order)
                    if write_index > -1:
                        explain_content = match.group(2)
                        questions_list[write_index]['answer'] = p_add_marked(explain_content)
                        last_area = 'explain'
                        # print('解析部分的order:', order)
                    else:
                        partly_error += f'第{order}题的解析未录入<br>'
                        result_flag = False
                # 匹配到解析：或【解析】x，比如解析：x。说明已经匹配到解析，这是另一种格式
                elif (pattern_explain_detach_no_number.search(p_text.strip()) or
                      pattern_explain_attach_no_number.search(p_text.strip())) or \
                        (pattern_explain_detach_no_number2.search(p_text.strip()) or
                         pattern_explain_attach_no_number2.search(p_text.strip())) and is_answer_section:
                    write_index = search_order_index_error(questions_list, order)
                    if write_index > -1:
                        if pattern_explain_detach_no_number.search(p_text.strip()):
                            match = pattern_explain_detach_no_number.search(p_text.strip())
                        elif pattern_explain_attach_no_number.search(p_text.strip()):
                            match = pattern_explain_attach_no_number.search(p_text.strip())
                        elif pattern_explain_detach_no_number2.search(p_text.strip()):
                            match = pattern_explain_detach_no_number2.search(p_text.strip())
                        else:
                            match = pattern_explain_attach_no_number2.search(p_text.strip())
                        explain_content = match.group(1)
                        questions_list[write_index]['explain'] = p_add_marked(explain_content)
                        last_area = 'explain'
                    else:
                        partly_error += f'第{order}题的解析未录入<br>'
                        result_flag = False
                # 匹配到参考答案，结算最后一题题干
                elif pattern_answer_vocational_rule.search(p_text.strip()) and not is_answer_section:
                    if question_dict['content'] != '':
                        questions_list.append(question_dict)
                    is_answer_section = True
                    precondition_collect_over = True
                    # print('匹配到参考答案')
                # 匹配到试题前置情境
                elif re.search(precondition_rule, p_text.strip(), re.M):
                    precondition_number_start = int(re.search(precondition_rule, p_text).group(2))
                    precondition_number_end = int(re.search(precondition_rule, p_text).group(3))
                    precondition_content = p_add_marked(p_text)
                    # 以下是初始化存储前置情境的变量，False表示已经情境范围未结束
                    precondition_collect_over = False
                # 非空行的文本内容，且未到参考答案部分，则添加到题干或情境中
                elif not is_answer_section:
                    if not precondition_collect_over:
                        precondition_content += p_add_marked(p_text)
                    elif len(question_dict['content']) > 0:
                        question_dict['content'] += p_add_marked(p_text)
                # 非空行的文本内容，且到达参考答案部分，根据上次填充的内容，本地跟上次一样
                elif is_answer_section:
                    if len(questions_list[write_index][last_area]) > 0:
                        questions_list[write_index][last_area] += p_add_marked(p_text)
            # 获取该段落内的所有的图片
            p_images = get_images_in_paragraph(element)
            if p_images:
                for p_image in p_images:
                    p_image_marked = img_add_marked(p_image)
                    if not precondition_collect_over and not is_answer_section:
                        precondition_content += p_add_marked(p_image_marked)
                    elif not is_answer_section and len(question_dict['content']) > 0:
                        question_dict['content'] += p_add_marked(p_image_marked)
                    elif is_answer_section and len(questions_list[write_index][last_area]) > 0:
                        questions_list[write_index][last_area] += p_image_marked
        # 以下获取表格内容，分类讨论放入题目中或收集答案
        elif ele_name == "w:tbl":
            table = get_table(element)
            # print('table not marked:', table)
            if len(table) > 0:
                table_marked = table_add_marked(table)
                if not precondition_collect_over and not is_answer_section:
                    precondition_content += p_add_marked(table_marked)
                elif not is_answer_section and len(question_dict['content']) > 0:
                    question_dict['content'] += p_add_marked(table_marked)
                elif is_answer_section and len(questions_list[write_index][last_area]) > 0:
                    questions_list[write_index][last_area] += table_marked
        node_index += 1
    # print('all answers over!')
    return result_flag, partly_error, questions_list, paper_dict


# 答案附题后，普高专用
def get_docx_text(path):
    # 以下变量用于记录前置情境内容
    precondition_number_start = precondition_number_end = -1
    precondition_content = ""
    precondition_collect_over = True  # 收集前置情境是否结束，True表示结束
    questions_list = []  # 存放所有题目
    question_dict = {"content": "", "answer": "", "explain": "", "type": 1, "reference": "", "difficulty_level": 3,
                     "knowledgepoint":  [], "knowledgepoint_cn":  [], 'bank_point': 1
                     }
    with zipfile.ZipFile(path, 'r') as zfile:
        zfile.extractall()
    # 以下读取所有图片文件，并一id为key,图片在服务器的url为value
    image_rels = xdom.parse('word/_rels/document.xml.rels')
    root1 = image_rels.documentElement
    store_images_to_dict(root1)

    # 初始化拿到第一个body标签
    xp = xdom.parse('word/document.xml')
    root = xp.documentElement
    bodies = root.getElementsByTagName("w:body")
    body = bodies[0]

    current_type_cn = '选择题'
    current_type = type_dic[current_type_cn]
    question_dict["type"] = current_type
    question_dict['type_cn'] = current_type_cn
    node_index = 0
    has_title = False    # 标记试题是否有标题行，如果没有标题为False;有标题True
    has_question_type = False   # 标记是否题型行，如果没有题型False,否则为True
    # 第一行试卷的标题填入“reference”字段，并填入paper的标题
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    # 用while是因为跳过空行段落,来到第一行
    while paragraph_text.strip() == '':
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    reference = paragraph_text.strip()
    question_dict["reference"] = reference
    paper_dict['title'] = reference

    # 第一行中提取年级，并存入"grade"字段
    if re.search(grade_rule, paragraph_text):
        grade = grade_dic[re.search(grade_rule, paragraph_text).group(1)]
        paper_dict["grade"] = grade
        has_title = True
    publish_time = ''
    publish_year = ''
    # 第一行中提取试卷的时间，字典中填入“publish_time”字段
    if re.search(pattern_date_year_rule, paragraph_text):
        publish_year = re.search(pattern_date_year_rule, paragraph_text).group(1)
        publish_time += publish_year
    if re.search(pattern_date_month_rule, paragraph_text):
        publish_month = re.search(pattern_date_month_rule, paragraph_text).group(1)
        if publish_year != '':
            publish_time = publish_year + "-" + publish_month + "-" + "1"
            paper_dict["publish_time"] = publish_time
            question_dict['publish_time'] = publish_time
            has_title = True
    # 第二行是题目的题型，字典中填入“type”字段
    # 包含没有题型，直接试题开始的特殊情况
    if has_title:
        node_index += 1
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    while paragraph_text.strip() == '':
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    if re.search(question_choice_or_cloze_rule, paragraph_text):
        current_type_cn = re.search(question_choice_or_cloze_rule, paragraph_text).group(1)
        question_dict["type_cn"] = current_type_cn
        question_dict["type"] = type_dic[current_type_cn]
        has_question_type = True
    # 从第3行开始收集题目，跳过空行
    # 包含没有题型行，直接试题开始的特殊情况
    if has_question_type:
        node_index += 1
    paragraph = body.childNodes[node_index]
    paragraph_text = get_text_in_paragraph(paragraph)
    while paragraph_text.strip() == '':
        node_index += 1
        paragraph = body.childNodes[node_index]
        paragraph_text = get_text_in_paragraph(paragraph)
    while node_index < len(body.childNodes):
        element = body.childNodes[node_index]
        ele_name = element.nodeName
        # 以下获取可见文本和图片，因此都在<w:p>便签内
        if ele_name == "w:p":
            p_text = get_text_in_paragraph(element)
            p_text = blank_char.sub(' ', p_text).replace("（", "(").replace('）', ')').replace('，', ',')
            # 破坏题干或选项中的html标签，即在<后加空格
            tag_index = find_first_html_tag_index(p_text)
            while len(p_text) > 0 and tag_index > -1:
                p_text = p_text[:tag_index+1] + '&nbsp;' + p_text[tag_index+1:]
                tag_index = find_first_html_tag_index(p_text)
            if len(p_text) > 0:
                # 匹配题型，如二、非选择题,先结算上一题，在清空字典。
                if re.match(question_type_rule, p_text.strip()):
                    if question_dict["content"]:
                        questions_list.append(question_dict)
                    current_type_cn = re.match(question_type_rule, p_text).group(1)
                    question_dict = {"content": "", "answer": "", "explain": "", "type": type_dic[current_type_cn],
                                     "type_cn": current_type_cn, "reference": reference, "difficulty_level": 3,
                                     "knowledgepoint":  [], "knowledgepoint_cn":  [], 'publish_time': publish_time,
                                     'bank_point': 1
                                     }
                # 匹配到题号，比如1.以下xxx, 先结算上一题的内容，添加到列表，再清空题目字典，最后把本行文本加入到新题目字典中，以及题号
                elif re.search(question_number_rule, p_text.strip(), re.M):
                    if len(question_dict["content"]) > 0:
                        questions_list.append(question_dict)
                        question_dict = {"content": "", "answer": "", "explain": "", "type_cn": current_type_cn,
                                         'type': type_dic[current_type_cn], "reference": reference,
                                         "difficulty_level": 3, "knowledgepoint":  [], 'publish_time': publish_time,
                                         "knowledgepoint_cn":  [], 'bank_point': 1
                                         }
                    content = re.search(question_number_rule, p_text.strip(), flags=re.M).group(4)
                    order = re.search(question_number_rule, p_text.strip(), flags=re.M).group(2)
                    # print('order:', order)
                    question_dict['order'] = int(order)
                    # 停止收集情境文本, 用于遇到普通文本是，判断是否收集到情境中
                    precondition_collect_over = True
                    # 在情境范围内，则情境问题放在最前面，并连接本次题号后的内容
                    if precondition_number_start <= int(order) <= precondition_number_end:
                        question_dict['content'] = precondition_content + p_add_marked(content)
                    # 情境超出题号范围，无须处理情境内容，只须把题号后的文本添加到新题的题干中
                    else:
                        question_dict['content'] = p_add_marked(content)
                    if int(order) == precondition_number_end:
                        precondition_content = ""

                # 匹配到答案
                elif re.search(answer_rule, p_text):
                    answer = re.search(answer_rule, p_text).group(1)
                    question_dict['answer'] = p_add_marked(answer)
                # 匹配到解析
                elif re.search(explain_rule, p_text):
                    explain = re.search(explain_rule, p_text).group(1)
                    question_dict['explain'] = p_add_marked(explain)
                # 匹配到难度
                elif re.search(difficulty_rule, p_text):
                    difficulty_level = re.search(difficulty_rule, p_text).group(1)
                    question_dict['difficulty_level'] = int(difficulty_level)
                # 匹配到试题前置情境
                elif re.search(precondition_rule, p_text.strip(), re.M):
                    precondition_number_start = int(re.search(precondition_rule, p_text).group(2))
                    precondition_number_end = int(re.search(precondition_rule, p_text).group(3))
                    precondition_content = p_add_marked(p_text)
                    # 以下是初始化存储前置情境的变量，False表示已经情境范围未结束
                    precondition_collect_over = False

                # 非空行的文本内容，则按照情境→解析→答案→题干的顺序填充
                else:
                    if not precondition_collect_over:
                        precondition_content += p_add_marked(p_text)
                    elif len(question_dict['explain']) > 0:
                        question_dict['explain'] += p_add_marked(p_text)
                    elif len(question_dict['answer']) > 0:
                        question_dict['answer'] += p_add_marked(p_text)
                    elif len(question_dict['content']) > 0:
                        question_dict['content'] += p_add_marked(p_text)
            # 获取该段落内的所有的图片
            p_images = get_images_in_paragraph(element)
            pictures_temp = ''
            for p_image in p_images:
                p_image_marked = img_add_marked(p_image)
                pictures_temp += p_image_marked
            if len(pictures_temp) > 0:
                if not precondition_collect_over:
                    precondition_content += pictures_temp
                elif question_dict["explain"]:
                    question_dict["explain"] += pictures_temp
                elif question_dict["answer"]:
                    question_dict["answer"] += pictures_temp
                elif question_dict["content"]:
                    question_dict["content"] += pictures_temp

        # 以下获取表格内容
        elif ele_name == "w:tbl":
            table = get_table(element)
            # print('table:', table)
            if len(table) > 0:
                table_marked = table_add_marked(table)
                # print('table_marked:', table_marked)
                if not precondition_collect_over:
                    precondition_content += table_marked
                elif question_dict["explain"] != '':
                    question_dict["explain"] += table_marked
                elif question_dict["answer"] != '':
                    question_dict["answer"] += table_marked
                elif question_dict["content"] != '':
                    question_dict['content'] += table_marked
        node_index += 1
    # 最后一题单独处理
    if len(question_dict["content"]) > 0:
        questions_list.append(question_dict)
        question_dict = {"content": "", "answer": "", "explain": "", "type_cn": '选择题', 'publish_time': publish_time,
                         'type': 1, "reference": reference, "difficulty_level": 3, "knowledgepoint":  [],
                         "knowledgepoint_cn":  [], 'bank_point': 1}
    # for i in range(len(questions_list)):
    #     item = questions_list[i]
    #     print(item['order'], ':', item)
    return True, 'ok', questions_list, paper_dict


if __name__ == "__main__":
    result = get_docx_text('test.docx')
